<?php

namespace App\Services;

use App\Project;
use App\ConvertTask;
use DB, Log;
use Exception;
use GuzzleHttp\Client;

class WordToPdfService
{
    const CONVERT_STATUS_CODE_CONVERTING = 1;
    const CONVERT_STATUS_CODE_COMPLETE = 2;

    private $lastException = null;

    public function __construct()
    {
        $this->apiKey = config('vars.pdf_server_key_product');
        $this->baseUrl = config('vars.pdf_server_domain') . "/file_convert/api";
    }

    public function getLastException()
    {
        return $this->lastException;
    }

    public function convert($wordFilePath, $pdfFilePath, $timeoutSeconds = 600)
    {
        $startTime = time();

        if (!file_exists($wordFilePath)) {
            Log::error('WordToPdfService.convert: File not found', ['file' => $wordFilePath]);
            $this->lastException = new Exception("File not found: $wordFilePath");
            return false;
        }

        $pathinfo = pathinfo($wordFilePath);
        $extension = strtolower($pathinfo['extension']);
        if (!in_array($extension, ['doc', 'docx'])) {
            Log::error('WordToPdfService.convert: Not a word file', ['file' => $wordFilePath]);
            $this->lastException = new Exception("Not a word file: $wordFilePath");
            return false;
        }

        $orderId = $this->createConvertOrder($wordFilePath);
//        $orderId = 'dcf0cee330234aaeb77d9c256242b62e';
        if (empty($orderId)) {
            Log::error('WordToPdfService.convert: Failed to create convert order', ['file' => $wordFilePath]);
            $this->lastException = new Exception("Failed to create convert order: $wordFilePath");
            return false;
        }

        while (time() - $startTime < $timeoutSeconds) {
            $resData = $this->queryConvertResult($orderId);
            if (empty($resData) || $resData->code != 0) {
                $errData = ['file' => $wordFilePath, 'result' => $resData];
                Log::error('WordToPdfService.convert: Bad convert result', $errData);
                $this->lastException = new Exception("Bad convert result: ".json_encode($errData, JSON_UNESCAPED_UNICODE));
                return false;
            }
            if ($resData->data->status_code == static::CONVERT_STATUS_CODE_COMPLETE) {
                $pdfUrl = $resData->data->target_file_url . "&key={$this->apiKey}";
                $tries = 0;
                while (true) {
                    $tries += 1;
                    try {
                        $client = new Client();
                        $client->get($pdfUrl, ['save_to' => $pdfFilePath]);
                        if (file_exists($pdfFilePath)) {
                            return true;
                        }
                    } catch (Exception $e) {
                        $errData = ['file' => $wordFilePath, 'pdf_url' => $pdfUrl, 'exception' => $e];
                        $this->lastException = new Exception("Failed to download converted file: ".json_encode($errData, JSON_UNESCAPED_UNICODE));
                        Log::error('WordToPdfService.convert: Failed to download converted file', $errData);
                    }
                    if ($tries >= 3) {
                        return false;
                    }
                    sleep(1);
                }
            }
            sleep(2);
        }

        Log::error('WordToPdfService.convert: Query convert result timeout', ['file' => $wordFilePath, 'timeoutSeconds' => $timeoutSeconds]);
        $this->lastException = new Exception("Query convert result timeout");
        return false;
    }

    private function queryConvertResult($orderId)
    {
        $client = new Client(['timeout' => 30]);
        $res = $client->get("{$this->baseUrl}/orders/$orderId", [
            'query' => ['key' => $this->apiKey],
        ]);
        $status = $res->getStatusCode();
        if ($status != 200) {
            Log::error('WordToPdfService.queryOrderStatus: Failed to get order id', ['order_id' => $orderId]);
            return null;
        }

        $statusCode = $res->getStatusCode();
        if ($statusCode != 200) {
            Log::error('WordToPdfService.queryOrderStatus: Bad status code', ['order_id' => $orderId, 'status_code' => $statusCode]);
            return null;
        }
        $resBody = (string)$res->getBody();
        $resData = json_decode($resBody);
        if (empty($resData->data->order_id)) {
            Log::error('WordToPdfService.queryOrderStatus: Bad response', ['order_id' => $orderId, 'body' => $resBody]);
            return null;
        }

        return $resData;
    }

    private function createConvertOrder($wordFilePath)
    {
        $pathinfo = pathinfo($wordFilePath);
        $extension = strtolower($pathinfo['extension']);

        $client = new Client(['timeout' => 30]);
        $fp = fopen($wordFilePath, 'r');
        $res = $client->post("{$this->baseUrl}/orders", [
            'query' => [
                'key' => $this->apiKey,
                'name' => basename($wordFilePath),
                'source_format' => $extension,
                'target_format' => 'pdf'
            ],
            'body' => $fp,
            'exceptions' => false,
        ]);

        $statusCode = $res->getStatusCode();
        if ($statusCode != 200) {
            Log::error('WordToPdfService.createConvertOrder: Bad status code', ['file' => $wordFilePath, 'status_code' => $statusCode]);
            return null;
        }
        $resBody = (string)$res->getBody();
        $resData = json_decode($resBody);
        if (empty($resData->data->order_id)) {
            Log::error('WordToPdfService.createConvertOrder: Bad response', ['file' => $wordFilePath, 'body' => $resBody]);
            return null;
        }

        $orderId = $resData->data->order_id;

        return $orderId;
    }

    /*
     * @deprecated
     */
    public function convertWordToPdf($wordFilePath, $resultPath, $awardId = null, $taskId = null)
    {
        $key = config('vars.pdf_server_key_product');
        Log::info("开始向word转pdf服务器发请求创建任务, wordFilePath: {$wordFilePath}");
        if (!file_exists($wordFilePath)) {
            Log::error("word文件不存在");
            return 'word文件不存在';
        }
        $orderId = $this->getOrderId($wordFilePath);
        if (empty($orderId)) {
            Log::error("生成pdf任务获取orderId失败");
            return '生成pdf任务获取orderId失败';
        }
        Log::info("创建生成pdf任务成功，任务id: {$orderId}");

        sleep(3);
        Log::info("开始根据生成pdf任务id确认任务状态");
        $pdfUrl = $this->getOrderStatusByOrderId($orderId);
        $pdfUrlNew = $pdfUrl . '&key=' . $key;
        Log::info("生成pdf任务已完成，pdf下载链接: {$pdfUrlNew}");

        Log::info("开始下载pdf存到本地，存储路径: {$resultPath}");
        $fileName = date('YmdHis') . '.pdf';
        $localPdfFileSavePath = $resultPath . '/' . $fileName;
        $this->downloadFile($pdfUrlNew, public_path($localPdfFileSavePath));
        Log::info("下载pdf成功");

        if (!empty($awardId)) {
            Log::info("奖项表中更新pdf存储路径");
            $this->saveAwardPdfPath($awardId, $localPdfFileSavePath, $taskId);
        }
        return null;
    }

    /*
     * @deprecated
     */
    public function getOrderId($wordFilePath)
    {
        $url = config('vars.pdf_server_domain') . "/file_convert/api/";
        $client = new Client(
            [
                'timeout' => 20,
                'base_uri' => $url
            ]
        );
        $key = config('vars.pdf_server_key_product');
        if (!file_exists($wordFilePath)) {
            return null;
        }
        $fileName = date('YmdHis');
        $resource = fopen($wordFilePath, 'r');
        $response = $client->post('orders', [
            'query' => [
                'key' => $key,
                'name' => $fileName,
                'source_format' => 'docx',
                'target_format' => 'pdf'
            ],
            'body' => $resource
        ]);
        $status = $response->getStatusCode();
        if ($status != 200) {
            return null;
        }
        Log::info("创建任务请求响应200");
        $returnInfoStr = (string)$response->getBody();
        $returnInfoArr = json_decode($returnInfoStr, true);
        $code = $returnInfoArr['code'];
        if ($code != 0) {
            return null;
        }
        $order = $returnInfoArr['data']['order_id'];
        return $order;
    }

    /*
     * @deprecated
     */
    public function getOrderStatusByOrderId($orderId)
    {
        $response = $this->queryOrderStatusByOrderId($orderId);
        $status = $response->getStatusCode();
        if ($status != 200) {
            return null;
        }
        $returnInfoStr = (string)$response->getBody();
        $returnInfoArr = json_decode($returnInfoStr, true);
        $code = $returnInfoArr['code'];
        if ($code != 0) {
            return null;
        }
        $statusCode = $returnInfoArr['data']['status_code'];
        if ($statusCode == 1) {
            Log::info("确认pdf任务状态为待完成");
            //待完成状态
            $startTime = microtime(true);
            $retryCount = 0;
            while (true) {
                $retryCount++;
                Log::info("重新确认pdf任务状态，第{$retryCount}次");
                $responseNew = $this->queryOrderStatusByOrderId($orderId);
                $status = $responseNew->getStatusCode();
                $endTime = microtime(true);
                if ($status == 200) {
                    $returnInfoStr = (string)$responseNew->getBody();
                    $returnInfoArr = json_decode($returnInfoStr, true);
                    $code = $returnInfoArr['code'];
                    if ($code == 0) {
                        $statusCode = $returnInfoArr['data']['status_code'];
                        if ($statusCode == 2) {
                            Log::info("第{$retryCount}次确认pdf任务状态结果为已完成");
                            $pdfUrl = $returnInfoArr['data']['target_file_url'];
                            return $pdfUrl;
                        }
                    }
                    Log::info("第{$retryCount}次确认pdf任务状态为未完成");
                }
                $runTime = ($endTime - $startTime) * 1000 . ' ms';
                if ($runTime > 60000 || $retryCount > 20) {
                    Log::info("重新确认pdf任务状态超时或超过10次");
                    break;
                }
                sleep(1);
            }
            return null;
        } else if ($statusCode == 2) {
            Log::info("确认任务状态为已完成");
            $pdfUrl = $returnInfoArr['data']['target_file_url'];
            return $pdfUrl;
        } else {
            Log::error("确认任务状态为失败或取消");
            return null;
        }
    }

    /*
     * @deprecated
     */
    public function queryOrderStatusByOrderId($orderId)
    {
        $url = config('vars.pdf_server_domain') . "/file_convert/api/";
        $client = new Client(
            [
                'timeout' => 20,
                'base_uri' => $url
            ]
        );
        $key = config('vars.pdf_server_key_product');
        $response = $client->get('orders/' . $orderId, [
            'query' => [
                'key' => $key
            ]
        ]);
        return $response;
    }

    /*
     * @deprecated
     */
    public function downloadFile($url, $saveFilePath)
    {
        $file = file_get_contents($url);
        file_put_contents($saveFilePath, $file);
    }

    /*
     * @deprecated
     */
    public function saveAwardPdfPath($awardId, $pdfPath, $taskId)
    {
        DB::beginTransaction();
        $application = Project::find($awardId);
        $application->pdf_file_save_path = $pdfPath;
        $application->save();
        $task = ConvertTask::find($taskId);
        $task->result = 'success';
        $task->error_info = null;
        $task->processed_at = date('Y-m-d H:i:s');
        $task->pdf_file_save_path = $pdfPath;
        $task->save();
        DB::commit();
    }
}